package cc.wanforme.st.server.util.encryt;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/** AES 堆成加密工具 <br>
 * 参考 https://wenku.baidu.com/view/d5a49c75f31dc281e53a580216fc700abb6852f0.html
 * @author wanne
 * @date 2022-04-12
 */
public class AESTool {
	private static final Charset CHARSET = StandardCharsets.UTF_8;
	private static final String ALGORITHM_TYPE = "AES";
	private static final String ALGORITHM_CIPHER = "AES/CBC/PKCS5Padding"; // 为什么没有 AES/CBC/PKCS7Padding
	
	private static final Decoder decoder = Base64.getDecoder();
	private static final Encoder encoder = Base64.getEncoder();
	
	/**
	 * @param rawStr 原生字符串
	 * @param key 
	 * @return
	 * @throws GeneralSecurityException
	 */
	public static String encrypt(String rawStr, String key, String iv) throws GeneralSecurityException {
		if(iv==null || iv.length()!=16) {
			throw new InvalidKeyException("iv应为16个字符，实际：" + (iv==null?0:iv.length()) );
		}
		if(key==null || key.length()!=16) {
			throw new InvalidKeyException("key应为16个字符，实际：" + (key==null?0:key.length()) );
		}
		
		byte[] keyBs = key.getBytes(CHARSET);
		SecretKeySpec keySpec = new SecretKeySpec(keyBs, ALGORITHM_TYPE);
		
		byte[] ivBs = iv.getBytes(CHARSET);
		IvParameterSpec ivPs = new IvParameterSpec(ivBs);
		
		Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
		cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivPs);
		
		byte[] encrypted = cipher.doFinal(rawStr.getBytes(CHARSET));
		String encryptedStr = encoder.encodeToString(encrypted);
		
		return encryptedStr;
	}
	
	/**
	 * @param str 已加密的字符串
	 * @param key
	 * @return
	 * @throws GeneralSecurityException
	 */
	public static String decrypt(String str, String key, String iv) throws GeneralSecurityException {
		if(iv==null || iv.length()!=16) {
			throw new InvalidKeyException("iv应为16个字符，实际：" + (iv==null?0:iv.length()) );
		}
		if(key==null || key.length()!=16) {
			throw new InvalidKeyException("key应为16个字符，实际：" + (key==null?0:key.length()) );
		}
		
		byte[] keyBs = key.getBytes(CHARSET);
		SecretKeySpec keySpec = new SecretKeySpec(keyBs, ALGORITHM_TYPE);

		byte[] ivBs = iv.getBytes(CHARSET);
		IvParameterSpec ivPs = new IvParameterSpec(ivBs);
		
		Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
		cipher.init(Cipher.DECRYPT_MODE, keySpec, ivPs);
		
		byte[] decodeBs = decoder.decode(str);
		byte[] originBs = cipher.doFinal(decodeBs);
		String re = new String(originBs, CHARSET);
		return re;
	}
	
	public static void main(String[] args) throws GeneralSecurityException {
		String abc = "01234567890abcd";
//		String abc = "2.	结果分析的颜色";
		String iv= "0123456789abcdef"; // 16位
		String key = "formeformeforme0"; // 16位
//		String abc = "123";
////		String abc = "2.	结果分析的颜色";
//		String iv= "D0W6h5yLtyDnAg8o"; // 16位
//		String key = "D0W6h5yLtyDnAg8o"; // 16位
		
		String en = encrypt(abc, key, iv);
		String den= decrypt(en, key, iv);
		System.out.println("encode: " + en); // R1lT07wIxuEhnWV2OVkd3w==
		System.out.println("decode: " + den);
		System.out.println("======================");
		
		AESHelper h = new AESHelper(key, iv);
		String encrypt2 = h.encrypt(abc);
		String decrypt2 = h.decrypt(encrypt2);
		System.out.println("encode: " + encrypt2);
		System.out.println("decode: " + decrypt2);
		System.out.println("======================");
	}
	
	/** AES对称加密辅助类 <br> 
	 * 用于效率优化
	 * @author wanne
	 * @date 2022-04-12
	 */
	static class AESHelper {
		// 加密 key
		private String key;
		// iv 向量
		private String ivStr;
		private SecretKeySpec keySpec;
		private IvParameterSpec iv;
		
		public AESHelper(String key) throws InvalidKeyException {
			this(key, key);
		}
		public AESHelper(String key, String ivStr) throws InvalidKeyException {
			if(ivStr==null || ivStr.length()!=16) {
				throw new InvalidKeyException("iv应为16个字符，实际：" + (ivStr==null?0:ivStr.length()) );
			}
			if(key==null || key.length()!=16) {
				throw new InvalidKeyException("key应为16个字符，实际：" + (key==null?0:key.length()) );
			}
			
			this.key = key;
			this.ivStr = ivStr;
			
			byte[] keyBs = key.getBytes(CHARSET);
			keySpec = new SecretKeySpec(keyBs, ALGORITHM_TYPE);
			
			byte[] ivBs = ivStr.getBytes(CHARSET);
			iv = new IvParameterSpec(ivBs);
		}
		
		public String encrypt(String rawStr) throws GeneralSecurityException {
			Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
			cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
			
			byte[] encrypted = cipher.doFinal(rawStr.getBytes(CHARSET));
			String encryptedStr = encoder.encodeToString(encrypted);
			
			return encryptedStr;
		}
		
		public String decrypt(String encryptStr) throws GeneralSecurityException {
			Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
			cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
			
			byte[] decodeBs = decoder.decode(encryptStr);
			
			byte[] originBs = cipher.doFinal(decodeBs);
			String re = new String(originBs, CHARSET);
			return re;
		}
		
		public String getKey() {
			return key;
		}
		public String getIvStr() {
			return ivStr;
		}
		
	}
}